﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Foundations.Net.Threading
{
    //http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I
    public class Metaphor : WaitHandle
    {
        int _index;
        string _name;
        Mutex _mutex;
        AutoResetEvent _event;

        public Metaphor(string name, int count)
        {
            _name = name;
            Mutex mutex = new Mutex(false, name);
            if (mutex.WaitOne(0, false))
            {
                _index = count;
                _mutex = mutex;
                _event = new AutoResetEvent(false);
                Context.SetData(Context.AllocateNamedDataSlot(name), new Action(() =>
                {
                    if (Interlocked.Decrement(ref _index) <= 0)
                    {
                        _event.Set();
                    }

                }));
            }
        }

        public override bool WaitOne()
        {
            return WaitOne(new TimeSpan(0, 0, 0, 0, -1));
        }

        public override bool WaitOne(int millisecondsTimeout)
        {
            return WaitOne(new TimeSpan(0, 0, 0, 0, millisecondsTimeout));
        }

        public override bool WaitOne(int millisecondsTimeout, bool exitContext)
        {
            return WaitOne(new TimeSpan(0, 0, 0, 0, millisecondsTimeout), exitContext);
        }

        public override bool WaitOne(TimeSpan timeout)
        {
            try
            {
                if (_mutex != null)
                {
                    _event.WaitOne(timeout);

                }
            }
            catch
            {
                throw;
            }
            finally
            {
                if (_mutex != null)
                {
                    _mutex.ReleaseMutex();
                    _mutex.Close();
                    _event.Close();
                    Context.FreeNamedDataSlot(_name);
                }

            }
            return _index == 0 && _mutex != null;
        }

        public override bool WaitOne(TimeSpan timeout, bool exitContext)
        {
            return WaitOne(timeout);
        }

        public static Metaphor operator --(Metaphor metaphor)
        {
            (Context.GetData(Context.GetNamedDataSlot(metaphor._name)) as Action ?? new Action(() => { }))();
            return metaphor;
        }

        protected override void Dispose(bool explicitDisposing)
        {
            if (_mutex != null)
            {
                _event.Dispose();
                _mutex.Dispose();
            }
            base.Dispose(explicitDisposing);
        }
    }
}
